<template>
  <div class="reflow-repaint-demo">
    <h2 class="section-title">DOM 回流与重绘</h2>
    <p class="section-intro">
      回流(Reflow)和重绘(Repaint)是浏览器渲染过程中的重要概念，理解它们对于性能优化至关重要。
    </p>

    <div class="concept-overview">
      <div class="concept-card reflow-card">
        <h3>🔄 回流 (Reflow)</h3>
        <p>当DOM元素的几何属性发生变化时，浏览器需要重新计算元素的位置和大小，这个过程叫做回流。</p>
        <div class="triggers">
          <strong>触发条件：</strong>
          <ul>
            <li>改变元素尺寸 (width, height, padding, margin)</li>
            <li>改变元素位置 (top, left, right, bottom)</li>
            <li>改变字体大小</li>
            <li>添加或删除DOM元素</li>
            <li>浏览器窗口尺寸变化</li>
          </ul>
        </div>
      </div>

      <div class="concept-card repaint-card">
        <h3>🎨 重绘 (Repaint)</h3>
        <p>当DOM元素的外观发生变化但不影响布局时，浏览器只需要重新绘制元素，这个过程叫做重绘。</p>
        <div class="triggers">
          <strong>触发条件：</strong>
          <ul>
            <li>改变颜色 (color, background-color)</li>
            <li>改变边框样式 (border-style)</li>
            <li>改变可见性 (visibility)</li>
            <li>改变阴影 (box-shadow)</li>
            <li>改变透明度 (opacity)</li>
          </ul>
        </div>
      </div>
    </div>

    <div class="demo-grid">
      <!-- 回流演示 -->
      <div class="demo-card">
        <h3>回流演示</h3>
        <div class="code-example">
          <div class="code-block">
            <pre><code>// 触发回流的操作
element.style.width = '200px';    // 改变宽度
element.style.height = '100px';   // 改变高度
element.style.padding = '20px';   // 改变内边距
element.style.fontSize = '18px';  // 改变字体大小

// 获取布局信息也会触发回流
const width = element.offsetWidth;
const height = element.offsetHeight;</code></pre>
          </div>
          <div class="demo-result">
            <div class="reflow-demo-container">
              <div ref="reflowBox" class="demo-box reflow-box">回流测试盒子</div>
              <div class="controls">
                <button @click="triggerReflow('width')">改变宽度</button>
                <button @click="triggerReflow('height')">改变高度</button>
                <button @click="triggerReflow('padding')">改变内边距</button>
                <button @click="triggerReflow('fontSize')">改变字体</button>
                <button @click="resetReflowBox">重置</button>
              </div>
              <div class="performance-info">
                <span>回流次数: {{ reflowCount }}</span>
                <span class="cost">性能消耗: 高 ⚠️</span>
              </div>
            </div>
          </div>
        </div>
      </div>

      <!-- 重绘演示 -->
      <div class="demo-card">
        <h3>重绘演示</h3>
        <div class="code-example">
          <div class="code-block">
            <pre><code>// 只触发重绘的操作
element.style.color = 'red';           // 改变文字颜色
element.style.backgroundColor = 'blue'; // 改变背景色
element.style.borderColor = 'green';    // 改变边框颜色
element.style.boxShadow = '0 2px 4px rgba(0,0,0,0.3)';

// 这些操作不会影响布局，只需要重新绘制</code></pre>
          </div>
          <div class="demo-result">
            <div class="repaint-demo-container">
              <div ref="repaintBox" class="demo-box repaint-box">重绘测试盒子</div>
              <div class="controls">
                <button @click="triggerRepaint('color')">改变文字颜色</button>
                <button @click="triggerRepaint('background')">改变背景</button>
                <button @click="triggerRepaint('border')">改变边框</button>
                <button @click="triggerRepaint('shadow')">添加阴影</button>
                <button @click="resetRepaintBox">重置</button>
              </div>
              <div class="performance-info">
                <span>重绘次数: {{ repaintCount }}</span>
                <span class="cost low">性能消耗: 低 ✅</span>
              </div>
            </div>
          </div>
        </div>
      </div>

      <!-- 性能对比 -->
      <div class="demo-card">
        <h3>性能对比测试</h3>
        <div class="code-example">
          <div class="code-block">
            <pre><code>// 低效方式：多次触发回流
for (let i = 0; i < 100; i++) {
  element.style.left = i + 'px';  // 每次都触发回流
}

// 高效方式：批量修改
element.style.cssText = 'left: 100px; top: 100px; width: 200px;';

// 或使用 CSS 类
element.className = 'optimized-style';</code></pre>
          </div>
          <div class="demo-result">
            <div class="performance-test">
              <div class="test-boxes">
                <div ref="slowBox" class="test-box">低效方式</div>
                <div ref="fastBox" class="test-box">高效方式</div>
              </div>
              <div class="controls">
                <button @click="runSlowTest" :disabled="testing">运行低效测试</button>
                <button @click="runFastTest" :disabled="testing">运行高效测试</button>
              </div>
              <div class="test-results" v-if="testResults.slow || testResults.fast">
                <div>低效方式耗时: {{ testResults.slow }}ms</div>
                <div>高效方式耗时: {{ testResults.fast }}ms</div>
                <div v-if="testResults.slow && testResults.fast" class="improvement">
                  性能提升: {{ Math.round((testResults.slow / testResults.fast - 1) * 100) }}%
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>

      <!-- 优化技巧 -->
      <div class="demo-card">
        <h3>优化技巧演示</h3>
        <div class="code-example">
          <div class="code-block">
            <pre><code>// 1. 使用 transform 代替改变位置
// 不好：触发回流
element.style.left = '100px';

// 好：只触发合成
element.style.transform = 'translateX(100px)';

// 2. 使用 opacity 代替 visibility
// 不好：触发重绘
element.style.visibility = 'hidden';

// 好：只触发合成
element.style.opacity = '0';</code></pre>
          </div>
          <div class="demo-result">
            <div class="optimization-demo">
              <div class="comparison">
                <div class="method">
                  <h4>传统方法 (触发回流)</h4>
                  <div ref="traditionalBox" class="optimize-box traditional">传统动画</div>
                  <button @click="animateTraditional">开始动画</button>
                </div>
                <div class="method">
                  <h4>优化方法 (只触发合成)</h4>
                  <div ref="optimizedBox" class="optimize-box optimized">优化动画</div>
                  <button @click="animateOptimized">开始动画</button>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>

      <!-- 检测工具 -->
      <div class="demo-card">
        <h3>性能检测工具</h3>
        <div class="code-example">
          <div class="code-block">
            <pre><code>// 使用 Performance API 监控
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.entryType === 'measure') {
      console.log(`${entry.name}: ${entry.duration}ms`);
    }
  }
});

observer.observe({ entryTypes: ['measure'] });

// 测量代码执行时间
performance.mark('start');
// ... 执行代码 ...
performance.mark('end');
performance.measure('operation', 'start', 'end');</code></pre>
          </div>
          <div class="demo-result">
            <div class="detection-tools">
              <h4>浏览器开发者工具</h4>
              <ul>
                <li><strong>Performance 面板:</strong> 记录和分析页面性能</li>
                <li><strong>Rendering 面板:</strong> 显示重绘和回流区域</li>
                <li><strong>Layers 面板:</strong> 查看合成层</li>
              </ul>

              <h4>实用检测代码</h4>
              <div class="detection-code">
                <button @click="measurePerformance">测量当前页面性能</button>
                <div v-if="performanceData" class="performance-data">
                  <div>FCP: {{ performanceData.fcp }}ms</div>
                  <div>LCP: {{ performanceData.lcp }}ms</div>
                  <div>DOM节点数: {{ performanceData.domNodes }}</div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>

    <div class="optimization-tips">
      <h3>性能优化最佳实践</h3>
      <div class="tips-grid">
        <div class="tip-card">
          <h4>🚀 避免频繁回流</h4>
          <ul>
            <li>批量修改DOM样式</li>
            <li>使用CSS类代替内联样式</li>
            <li>避免在循环中修改样式</li>
          </ul>
        </div>
        <div class="tip-card">
          <h4>⚡ 使用高性能属性</h4>
          <ul>
            <li>transform 代替 left/top</li>
            <li>opacity 代替 visibility</li>
            <li>will-change 提示浏览器</li>
          </ul>
        </div>
        <div class="tip-card">
          <h4>🎯 合理使用合成层</h4>
          <ul>
            <li>transform3d() 创建合成层</li>
            <li>避免过多合成层</li>
            <li>使用 contain 属性</li>
          </ul>
        </div>
        <div class="tip-card">
          <h4>📊 性能监控</h4>
          <ul>
            <li>使用 Performance API</li>
            <li>监控 Core Web Vitals</li>
            <li>定期性能审计</li>
          </ul>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'

const reflowBox = ref<HTMLElement>()
const repaintBox = ref<HTMLElement>()
const slowBox = ref<HTMLElement>()
const fastBox = ref<HTMLElement>()
const traditionalBox = ref<HTMLElement>()
const optimizedBox = ref<HTMLElement>()

const reflowCount = ref(0)
const repaintCount = ref(0)
const testing = ref(false)
const testResults = ref<{ slow?: number; fast?: number }>({})
const performanceData = ref<any>(null)

const triggerReflow = (type: string) => {
  if (!reflowBox.value) return

  reflowCount.value++

  switch (type) {
    case 'width':
      reflowBox.value.style.width = Math.random() * 100 + 100 + 'px'
      break
    case 'height':
      reflowBox.value.style.height = Math.random() * 50 + 50 + 'px'
      break
    case 'padding':
      reflowBox.value.style.padding = Math.random() * 20 + 10 + 'px'
      break
    case 'fontSize':
      reflowBox.value.style.fontSize = Math.random() * 10 + 14 + 'px'
      break
  }
}

const resetReflowBox = () => {
  if (!reflowBox.value) return
  reflowBox.value.style.cssText = ''
  reflowCount.value = 0
}

const triggerRepaint = (type: string) => {
  if (!repaintBox.value) return

  repaintCount.value++

  const colors = ['red', 'blue', 'green', 'purple', 'orange']
  const randomColor = colors[Math.floor(Math.random() * colors.length)]

  switch (type) {
    case 'color':
      repaintBox.value.style.color = randomColor
      break
    case 'background':
      repaintBox.value.style.backgroundColor = randomColor
      break
    case 'border':
      repaintBox.value.style.borderColor = randomColor
      break
    case 'shadow':
      repaintBox.value.style.boxShadow = `0 ${Math.random() * 10}px ${Math.random() * 20}px rgba(0,0,0,0.3)`
      break
  }
}

const resetRepaintBox = () => {
  if (!repaintBox.value) return
  repaintBox.value.style.cssText = ''
  repaintCount.value = 0
}

const runSlowTest = async () => {
  if (!slowBox.value) return

  testing.value = true
  const start = performance.now()

  // 低效方式：每次修改都触发回流
  for (let i = 0; i < 100; i++) {
    slowBox.value.style.left = i + 'px'
    slowBox.value.style.top = i + 'px'
    await new Promise((resolve) => setTimeout(resolve, 1))
  }

  const end = performance.now()
  testResults.value.slow = Math.round(end - start)
  testing.value = false
}

const runFastTest = async () => {
  if (!fastBox.value) return

  testing.value = true
  const start = performance.now()

  // 高效方式：批量修改
  fastBox.value.style.cssText = 'left: 100px; top: 100px; transition: all 0.1s;'
  await new Promise((resolve) => setTimeout(resolve, 100))

  const end = performance.now()
  testResults.value.fast = Math.round(end - start)
  testing.value = false
}

const animateTraditional = () => {
  if (!traditionalBox.value) return

  let pos = 0
  const animate = () => {
    if (pos < 200) {
      traditionalBox.value!.style.left = pos + 'px'
      pos += 2
      requestAnimationFrame(animate)
    } else {
      traditionalBox.value!.style.left = '0px'
    }
  }
  animate()
}

const animateOptimized = () => {
  if (!optimizedBox.value) return

  optimizedBox.value.style.transform = 'translateX(200px)'
  setTimeout(() => {
    optimizedBox.value!.style.transform = 'translateX(0px)'
  }, 1000)
}

const measurePerformance = () => {
  const navigation = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming
  const paint = performance.getEntriesByType('paint')

  performanceData.value = {
    fcp:
      paint.find((entry) => entry.name === 'first-contentful-paint')?.startTime.toFixed(2) || 'N/A',
    lcp: 'N/A', // LCP需要专门的API
    domNodes: document.querySelectorAll('*').length,
  }
}

onMounted(() => {
  // 初始化性能监控
  if ('PerformanceObserver' in window) {
    const observer = new PerformanceObserver((list) => {
      // 可以在这里处理性能数据
    })
    observer.observe({ entryTypes: ['measure', 'navigation'] })
  }
})
</script>

<style scoped lang="scss">
.reflow-repaint-demo {
  .section-title {
    font-size: 2rem;
    color: #333;
    margin-bottom: 1rem;
    border-bottom: 3px solid #f44336;
    padding-bottom: 0.5rem;
  }

  .section-intro {
    font-size: 1.1rem;
    color: #666;
    margin-bottom: 2rem;
    line-height: 1.6;
  }

  .concept-overview {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
    gap: 2rem;
    margin-bottom: 2rem;

    .concept-card {
      border-radius: 8px;
      padding: 1.5rem;

      &.reflow-card {
        background: #ffebee;
        border-left: 4px solid #f44336;
      }

      &.repaint-card {
        background: #e8f5e8;
        border-left: 4px solid #4caf50;
      }

      h3 {
        margin: 0 0 1rem 0;
        font-size: 1.3rem;
      }

      p {
        margin-bottom: 1rem;
        line-height: 1.5;
      }

      .triggers {
        strong {
          color: #333;
        }

        ul {
          margin: 0.5rem 0;
          padding-left: 1.5rem;

          li {
            margin-bottom: 0.25rem;
            font-size: 0.9rem;
          }
        }
      }
    }
  }

  .demo-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
    gap: 2rem;
    margin-bottom: 2rem;
  }

  .demo-card {
    border: 1px solid #e0e0e0;
    border-radius: 8px;
    padding: 1.5rem;
    background: #fafafa;

    h3 {
      margin: 0 0 1rem 0;
      color: #f44336;
      font-size: 1.3rem;
    }

    .code-example {
      display: flex;
      flex-direction: column;
      gap: 1rem;
    }

    .code-block {
      background: #2d3748;
      border-radius: 6px;
      padding: 1rem;
      overflow-x: auto;

      pre {
        margin: 0;
        color: #e2e8f0;
        font-family: 'Fira Code', monospace;
        font-size: 0.9rem;
        line-height: 1.4;
      }
    }

    .demo-result {
      background: white;
      border: 1px solid #e0e0e0;
      border-radius: 6px;
      padding: 1rem;
      min-height: 100px;
    }
  }

  .optimization-tips {
    .tips-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
      gap: 1rem;
    }

    .tip-card {
      background: #fff3e0;
      border-radius: 8px;
      padding: 1.5rem;
      border-left: 4px solid #ff9800;

      h4 {
        margin: 0 0 0.5rem 0;
        color: #f57c00;
        font-size: 1.1rem;
      }

      ul {
        margin: 0;
        padding-left: 1.5rem;

        li {
          margin-bottom: 0.25rem;
          color: #666;
          font-size: 0.9rem;
        }
      }
    }
  }
}

// 演示样式
.demo-box {
  width: 150px;
  height: 80px;
  background: #e3f2fd;
  border: 2px solid #2196f3;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 500;
  margin-bottom: 1rem;
  transition: all 0.3s ease;
}

.controls {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 1rem;

  button {
    padding: 6px 12px;
    border: 1px solid #ddd;
    border-radius: 4px;
    background: white;
    cursor: pointer;
    font-size: 0.9rem;
    transition: all 0.2s;

    &:hover {
      background: #f5f5f5;
    }

    &:disabled {
      opacity: 0.5;
      cursor: not-allowed;
    }
  }
}

.performance-info {
  display: flex;
  gap: 1rem;
  font-size: 0.9rem;

  .cost {
    font-weight: 500;

    &.low {
      color: #4caf50;
    }

    &:not(.low) {
      color: #f44336;
    }
  }
}

.performance-test {
  .test-boxes {
    display: flex;
    gap: 2rem;
    margin-bottom: 1rem;

    .test-box {
      position: relative;
      width: 100px;
      height: 60px;
      background: #ffcdd2;
      border: 2px solid #f44336;
      border-radius: 6px;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 0.9rem;
      font-weight: 500;
    }
  }

  .test-results {
    background: #f5f5f5;
    padding: 1rem;
    border-radius: 6px;
    margin-top: 1rem;

    div {
      margin-bottom: 0.5rem;
      font-size: 0.9rem;
    }

    .improvement {
      color: #4caf50;
      font-weight: 500;
    }
  }
}

.optimization-demo {
  .comparison {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 2rem;

    .method {
      text-align: center;

      h4 {
        margin: 0 0 1rem 0;
        color: #333;
        font-size: 1rem;
      }

      .optimize-box {
        position: relative;
        width: 120px;
        height: 60px;
        background: #e1f5fe;
        border: 2px solid #03a9f4;
        border-radius: 6px;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 0.9rem;
        margin: 0 auto 1rem auto;

        &.optimized {
          transition: transform 0.5s ease;
        }
      }

      button {
        padding: 8px 16px;
        background: #03a9f4;
        color: white;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        font-size: 0.9rem;

        &:hover {
          background: #0288d1;
        }
      }
    }
  }
}

.detection-tools {
  h4 {
    margin: 1rem 0 0.5rem 0;
    color: #333;
  }

  ul {
    margin: 0 0 1rem 0;
    padding-left: 1.5rem;

    li {
      margin-bottom: 0.5rem;
      line-height: 1.4;

      strong {
        color: #2196f3;
      }
    }
  }

  .detection-code {
    button {
      padding: 8px 16px;
      background: #2196f3;
      color: white;
      border: none;
      border-radius: 4px;
      cursor: pointer;
      margin-bottom: 1rem;

      &:hover {
        background: #1976d2;
      }
    }

    .performance-data {
      background: #f5f5f5;
      padding: 1rem;
      border-radius: 6px;
      font-family: monospace;

      div {
        margin-bottom: 0.5rem;
      }
    }
  }
}
</style>
